﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Reflection;
using System.ComponentModel;
using Xceed.Silverlight.Data;

namespace Xceed.Silverlight.DataGrid
{
  internal class ItemsSourceHelper
  {
    internal static IDictionary<string, FieldDescriptor> GetFieldsForItemType( Type itemType )
    {
      if( itemType == null )
        throw new ArgumentNullException( "itemType" );

      IEnumerable<FieldDescriptor> returnedFieldDescriptors;

      if( itemType.IsInterface )
      {
        returnedFieldDescriptors = ItemsSourceHelper.GetFieldsForInterface( itemType );
      }
      else if( ItemsSourceHelper.IsValueType( itemType ) )
      {
        FieldDescriptor valueTypeFieldDescriptor = new FieldDescriptor( ".",
                                                                        string.Empty,
                                                                        null,
                                                                        ".",
                                                                        true,
                                                                        false,
                                                                        true );

        List<FieldDescriptor> returnValue = new List<FieldDescriptor>();
        returnValue.Add( valueTypeFieldDescriptor );

        returnedFieldDescriptors = returnValue;
      }
      else
      {
        returnedFieldDescriptors = ItemsSourceHelper.GetFieldDescriptors( itemType.GetProperties() );
      }

      IDictionary<string, FieldDescriptor> fieldDescriptorsDictionary = new Dictionary<string, FieldDescriptor>( 16 );

      foreach( FieldDescriptor fieldDescriptor in returnedFieldDescriptors )
      {
        fieldDescriptorsDictionary.Add( fieldDescriptor.Name, fieldDescriptor );
      }

      return fieldDescriptorsDictionary;
    }

    internal static FieldDescriptor GetField( Type itemType, string fieldName )
    {
      IDictionary<string, FieldDescriptor> items = ItemsSourceHelper.GetFieldsForItemType( itemType );
      if( items != null )
      {
        foreach( var item in items )
        {
          if( item.Key != fieldName )
            continue;

          return item.Value;
        }
      }

      return null;
    }

    private static IEnumerable<FieldDescriptor> GetFieldDescriptors( PropertyInfo[] properties )
    {
      int fieldCount = properties.Length;

      List<FieldDescriptor> fieldDescriptors = new List<FieldDescriptor>( fieldCount );

      ItemsSourceHelper.ExtractFieldDescriptors( properties, fieldDescriptors );

      return fieldDescriptors;
    }

    private static IEnumerable<FieldDescriptor> GetFieldsForInterface( Type itemType )
    {
      List<string> fieldDescriptorNames = new List<string>();

      Type[] interfaces = itemType.GetInterfaces();

      Type[] allInterfaces = new Type[ interfaces.Length + 1 ];
      allInterfaces[ 0 ] = itemType;
      interfaces.CopyTo( allInterfaces, 1 );

      foreach( Type interfaceType in allInterfaces )
      {
        List<FieldDescriptor> fieldDescriptors = new List<FieldDescriptor>();

        ItemsSourceHelper.ExtractFieldDescriptors( interfaceType.GetProperties(), fieldDescriptors );

        foreach( FieldDescriptor fieldDescriptor in fieldDescriptors )
        {
          if( !fieldDescriptorNames.Contains( fieldDescriptor.Name ) )
          {
            yield return fieldDescriptor;
          }
        }
      }
    }

    private static void ExtractFieldDescriptors(
     PropertyInfo[] properties,
     List<FieldDescriptor> fieldDescriptors )
    {
      int propertyCount = properties.Length;

      for( int i = 0; i < propertyCount; i++ )
      {
        PropertyInfo propertyInfo = properties[ i ];

        if( !ItemsSourceHelper.AutoGenerateFieldDisplay( propertyInfo ) )
          continue;

        string name = propertyInfo.Name;

        Type type = propertyInfo.PropertyType;

        bool isBrowsable = true;

        object[] browsableAttributes = type.GetCustomAttributes( typeof( BrowsableAttribute ), true ); //second parameter ignored, per documentation

        foreach( BrowsableAttribute browsableAttribute in browsableAttributes )
        {
          isBrowsable &= browsableAttribute.Browsable;
        }

        bool readOnly = ItemsSourceHelper.IsPropertyReadOnly( propertyInfo );

        fieldDescriptors.Add( new FieldDescriptor( name,
                                                   name,
                                                   propertyInfo,
                                                   name,
                                                   readOnly,
                                                   false,
                                                   isBrowsable ) );
      }
    }

    internal static bool IsPropertyReadOnly( PropertyInfo propertyInfo )
    {
      bool canWrite = propertyInfo.CanWrite;
      if( canWrite )
      {
        canWrite = ( propertyInfo.GetSetMethod() != null );
      }
      return !canWrite;
    }

    internal static bool IsValueType( Type type )
    {
      bool isValueType = type.IsValueType;

      return ( ( ( isValueType ) && ( type.IsPrimitive ) ) ||
        ( ( isValueType ) &&
        ( ( type == typeof( decimal ) ) ||
        ( type == typeof( DateTime ) ) ||
        ( type == typeof( TimeSpan ) ) ||
        ( type == typeof( Guid ) ) ) ) ||
        ( type == typeof( string ) ) );
    }

    internal static bool IsIComparable( Type type )
    {

      Type[] interfaces = type.GetInterfaces();

      foreach( Type interfaceType in interfaces )
      {
        if( ( interfaceType == typeof( IComparable ) ) || ( interfaceType == typeof( IComparable<> ) ) )
          return true;
      }

      return false;
    }

    internal static bool IsNullableType( Type type, Func<Type, bool> interrogate )
    {
      bool isNullableType = ( type.IsGenericType && ( type.GetGenericTypeDefinition() == typeof( Nullable<> ) ) );

      bool isNullableValueType = false;

      if( isNullableType )
      {
        Type[] genericTypeArguments = type.GetGenericArguments();

        if( genericTypeArguments.Length == 1 )
        {
          isNullableValueType = interrogate( genericTypeArguments[ 0 ] );
        }

      }

      return isNullableValueType;
    }

    private static bool AutoGenerateFieldDisplay( MemberInfo memberInfo )
    {
      bool autoGenerateField = true;
      Type displayAttributeType = null;

      if( SharedMetadata.DisplayAttributeAssemblyQualifiedName != null )
      {
        try
        {
          displayAttributeType = Type.GetType( SharedMetadata.DisplayAttributeAssemblyQualifiedName );
        }
        catch
        {
          displayAttributeType = null;
        }
      }

      if( displayAttributeType != null )
      {
        Attribute[] displayAttributes = Attribute.GetCustomAttributes( memberInfo, displayAttributeType, true );
        foreach( Attribute att in displayAttributes )
        {
          PropertyInfo autoGeneratedFieldProperty = att.GetType().GetProperty( "AutoGenerateField" );
          bool autoGeneratedFieldValue = ( bool )autoGeneratedFieldProperty.GetValue( att, null );
          autoGenerateField &= autoGeneratedFieldValue;
        }
      }

      return autoGenerateField;
    }
  }
}
